package nec.aagent;

import nec.aagent.core.dpm.MyDeviceAdminReceiver;
import nec.aagent.core.gcm.CommonUtils;
import nec.aagent.core.gcm.DeviceRegisterService;
import nec.aagent.core.gcm.GcmConstant;
import nec.aagent.core.service.location.ILocationTracerService;
import nec.aagent.core.service.location.LocationChangedReceiver;
import nec.aagent.core.service.location.LocationSetting;
import nec.aagent.core.service.location.LocationTracerService;
import android.app.Activity;
import android.app.PendingIntent;
import android.app.Service;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

import com.google.android.gcm.GCMRegistrar;

public class MainActivity extends FragmentActivity {

    static final String TAG = GcmConstant.TAG;
    private static final int REQUEST_CODE_DEVICE_ADMIN = 1;
    private static final int REQUEST_CODE_PREFERENCE = 2;

    /**
     * For debug purpose
     */
    private final BroadcastReceiver mHandleMessageReceiver =  new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String newMessage = intent.getExtras().getString(CommonUtils.EXTRA_MESSAGE);
            mDisplay.append(newMessage + "\n");
        }
    };

    private LocationManager mLocationManager;
    /**
     * Location setting instance
     */
    private static LocationSetting LOCATION = LocationSetting.instance();
    /**
     * We will receive location update via Broadcast Receiver
     */
    private PendingIntent mLocationIntent;

    /**
     * Location tracer service
     */
    private ILocationTracerService mLocationService;
    private boolean mLocationServiceBound = false;

    /**
     * Location service connection
     */
    private ServiceConnection mLocationServiceConnection = new ServiceConnection() {
        public void onServiceDisconnected(ComponentName name) {
            Log.i(TAG, "LocationTracerService: onServiceDisconnected");
            mLocationService = null;
        }

        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i(TAG, "LocationTracerService: onServiceConnected");
            mLocationService = ILocationTracerService.Stub.asInterface(service);
        }
    };

    private TextView mDisplay;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // request for device admin
        requestDeviceAdmin();

        registerReceiver(mHandleMessageReceiver,
                new IntentFilter(CommonUtils.DISPLAY_MESSAGE_ACTION));

        mLocationManager = (LocationManager) getSystemService(Service.LOCATION_SERVICE);
        Intent intent = new Intent(this, LocationChangedReceiver.class);
        mLocationIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

        // set up location tracer service
        loadLocationPrefs();
        startLocationService();

        mDisplay = (TextView) findViewById(R.id.tv_display);

        // Register this device on GCM server and our App Engine server
        doGcmRegistration();
    }

    @Override
    protected void onDestroy() {

        // THIS STATEMENT CAUSES EXCEPTION
        //GCMRegistrar.onDestroy(this);

        unregisterReceiver(mHandleMessageReceiver);

        unregisterLocationUpdate();
        stopLocationService();

        super.onDestroy();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch(item.getItemId()) {
        case R.id.menu_register:
            GCMRegistrar.register(this, GcmConstant.SENDER_ID);
            return true;
        case R.id.menu_unregister:
            GCMRegistrar.unregister(this);
            return true;
        case R.id.menu_clear:
            mDisplay.setText(null);
            return true;
        case R.id.menu_dpm:
            startActivity(new Intent(this, DevicePolicyActivity.class));
            return true;
        case R.id.menu_camera:
            startActivity(new Intent(this, CameraActivity.class));
            return true;
        case R.id.menu_settings:
            startActivityForResult(new Intent(this, PrefsActivity.class), REQUEST_CODE_PREFERENCE);
            return true;
        case R.id.menu_exit:
            finish();
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
        case REQUEST_CODE_DEVICE_ADMIN:
            if (resultCode == Activity.RESULT_OK) {
                Log.i(TAG, "Administration enabled!");
            } else {
                Log.i(TAG, "Administration enable FAILED!");
                finish();
            }
            return;
        case REQUEST_CODE_PREFERENCE:
            loadLocationPrefs();
            return;
        default:
            break;
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

    private void requestDeviceAdmin() {
        DevicePolicyManager dpm = (DevicePolicyManager)getSystemService(Context.DEVICE_POLICY_SERVICE);
        ComponentName mDeviceAdminComponent = new ComponentName(this, MyDeviceAdminReceiver.class);

        if (!dpm.isAdminActive(mDeviceAdminComponent)) {
            // Activate device administration
            Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
            intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mDeviceAdminComponent);
            intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "");
            startActivityForResult(intent, REQUEST_CODE_DEVICE_ADMIN);
        }
    }
    /**
     * Start {@link LocationTracerService}
     */
    private void startLocationService() {
        Log.i(TAG, "Start LocationTracerService");
        Intent intent = new Intent(ILocationTracerService.class.getName());
        startService(intent);
    }

    /**
     * Start {@link LocationTracerService}
     */
    private void stopLocationService() {
        Log.i(TAG, "Stop LocationTracerService");
        Intent intent = new Intent(ILocationTracerService.class.getName());
        stopService(intent);
    }

    /**
     * Bind to {@link LocationTracerService}
     */
    private void bindLocationService() {
        Log.i(TAG, "Bind to LocationTracerService");
        Intent intent = new Intent(ILocationTracerService.class.getName());
        mLocationServiceBound = bindService(intent, mLocationServiceConnection, Service.BIND_AUTO_CREATE);
    }

    /**
     * Unbind from {@link LocationTracerService} and unregister location update
     */
    private void unbindLocationService() {
        unbindLocationService(true);
    }

    private void unbindLocationService(boolean unregisterLocationUpdate) {
        if (mLocationServiceBound) {
            unbindService(mLocationServiceConnection);
            mLocationServiceBound = false;
        }
        if (unregisterLocationUpdate) {
            unregisterLocationUpdate();
        }
    }

    private void doGcmRegistration() {
        checkNotNull(GcmConstant.SERVER_URL, "SERVER_URL");
        checkNotNull(GcmConstant.SENDER_ID, "SENDER_ID");

        // Make sure the device has the proper dependencies.
        GCMRegistrar.checkDevice(this);

        // Make sure the manifest was properly set - comment out this line
        // while developing the app, then uncomment it when it's ready.
        GCMRegistrar.checkManifest(this);

        // Retrieve registration ID from GCM server
        final String regId = GCMRegistrar.getRegistrationId(this);

        if (regId.equals("")) {
            // Automatically registers application on startup.
            GCMRegistrar.register(this, GcmConstant.SENDER_ID);
        } else {
            // Device is already registered on GCM, check server.
            if (GCMRegistrar.isRegisteredOnServer(this)) {
                // Skips registration.
                mDisplay.append(getString(R.string.already_registered) + "\n");
            } else {
                // Try to register again
                Intent serviceIntent = new Intent(this, DeviceRegisterService.class);
                serviceIntent.putExtra(B.REG_ID, regId);
                DeviceRegisterService.runIntentService(this, serviceIntent);
            }
        }
    }

    private void loadLocationPrefs() {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);

        LOCATION.gps_provider = prefs.getBoolean(getString(R.string.pref_gps_key), true);
        LOCATION.network_provider = prefs.getBoolean(getString(R.string.pref_network_key), false);
        LOCATION.passive_mode = prefs.getBoolean(getString(R.string.pref_passive_key), false);

        Log.i(TAG, "gps: " + LOCATION.gps_provider + "; network: " + LOCATION.network_provider + "; passive: " + LOCATION.passive_mode);

        String temp = prefs.getString(getString(R.string.pref_periodic_time_key), getString(R.string.pref_periodic_option_default_value));
        long seconds = Long.parseLong(temp);
        LOCATION.min_time = 1000 * seconds;
        Log.i(TAG, "min time: " + seconds + " (second)");

        temp = prefs.getString(getString(R.string.pref_min_distance_key), getString(R.string.pref_min_distance_option_default_value));
        LOCATION.min_distance = Float.parseFloat(temp);
        Log.i(TAG, "min distance: " + LOCATION.min_distance + " (meter)");

        unregisterLocationUpdate();
        registerLocationUpdate();
    }

    private void registerLocationUpdate() {
        Log.i(TAG, "register location update");

        if (LOCATION.gps_provider) {
            mLocationManager.requestLocationUpdates(
                    LocationManager.GPS_PROVIDER, 
                    LOCATION.min_time, 
                    LOCATION.min_distance, 
                    mLocationIntent);
        }
        if (LOCATION.network_provider) {
            mLocationManager.requestLocationUpdates(
                    LocationManager.NETWORK_PROVIDER, 
                    LOCATION.min_time, 
                    LOCATION.min_distance, 
                    mLocationIntent);
        }
        if (LOCATION.passive_mode) {
            mLocationManager.requestLocationUpdates(
                    LocationManager.PASSIVE_PROVIDER, 
                    LOCATION.min_time, 
                    LOCATION.min_distance, 
                    mLocationIntent);
        }
    }

    private void unregisterLocationUpdate() {
        Log.i(TAG, "unregister location update");
        mLocationManager.removeUpdates(mLocationIntent);
    }

    private void checkNotNull(Object reference, String name) {
        if (reference == null) {
            throw new NullPointerException(
                    getString(R.string.error_config, name));
        }
    }
}
